CSS Modules
# 1、什么是 CSS Modules?
所有的 class 的名称和动画的名称默认属于本地作用域的 CSS 文件。所以 CSS Modules 不是一个官方的规范,也不是浏览器的一种机制,它是一种构建步骤中的一个进程(构建通常需要 webpack 或者 browserify 的帮助),通过构建工具的帮助,可以将 class 的名字或者选择器的名字作用域化(类似命名空间化),通过构建工具来使指定class 达到 scope 的过程。
# 2、为什么引入 CSS Modules?
全局样式冲突
webpack 进行打包时,将所有js文件导入到入口App.js文件中,样式也会统一加载到入口中,根据css的layout规则,后面的样式会覆盖掉前面的样式声明,造成全局样式的覆盖问题。
嵌套层次过深的选择器
为了解决全局样式的冲突问题,不得不引入一些特地命名namespace来区分,但是往往有些namespace命名得不够清晰,就会造成要想下一个样式不会覆盖,就要再加一个新的命名空间来进行区分,最终可能一个元素的显示样式嵌套特别深。
嵌套层次过深会造成的问题:
- 根据CSS选择器的解析规则可以知道,层级越深,比较的次数也就越多,影响整个页面的渲染
- 增加了不必要的字节开销
- 语义混乱 可扩展性不好,约束越多,扩展性越差
无法共享变量
复杂组件要使用 JS 和 CSS 来共同处理样式,就会造成有些变量在JavaScript和CSS中冗余
代码冗余,难以维护
# 3、CSS Modules 的优势
- 解决全局命名冲突问题,CSS Modules 只关心组件本身 命名唯一
- 模块化可以使用 composes 来引入自身模块中的样式以及另一个模块的样式
- 解决嵌套层次过深的问题,使用扁平化的类名
# 4、如何使用 CSS Modules
创建一个初始项目
├── build
│ └── built.js
├── index.html
├── node_modules
├── package-lock.json
├── package.json
├── src
│ ├── index.js
│ └── css
└── webpack.config.js
2
3
4
5
6
7
8
9
10
index.js 作为程序入口,css 文件夹存放样式文件,webpack.config.js 作为 webpack 的配置文件
index.css
.title{
font-size: 20px;
font-weight: bold;
}
2
3
4
index.js
// 通过对象的形式引入 css
import Index from '../css/index.css'
const html = `<h1 class="${Index.title}">CSS Modules</h1>`
document.querySelector('body').innerHTML = html
2
3
4
5
6
webpack.config.js
设置 css-loader 的 modules 属性为 true 开启 CSS Modules 功能
const {resolve} = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
entry: './src/js/index.js',
output: {
filename: 'built.js',
path: resolve(__dirname, 'build'),
},
module: {
rules: [
{
test: /\.css$/,
use: [
// 用这个loader取代style-loader,将js中的css提取成单独文件
{
loader: MiniCssExtractPlugin.loader,
options: {
publicPath: '../'
}
},
{
loader: 'css-loader',
options: {
modules: true
}
}
],
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: './src/index.html'
}),
new MiniCssExtractPlugin({
filename: 'css/main.css'
})
],
mode: 'development'
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# 5、CSS Modules 作用域
全局作用域
使用
:global(.className)的语法,声明一个全局规则,这样声明的 class,都不会被编译成哈希字符串局部作用域
:local(.className)等同于.className
# 6、定制哈希类名
css-loader 默认的哈希算法是 [hash:base64],这会将 .title 编译成 ._3cgV55LcqGee3dnKitPjdl 这样的字符串
在 webpack.config.js 中可以定制哈希字符串的格式
{
loader: 'css-loader',
options: {
modules: {
localIdentName: '[path][name]--[local]--[hash:base64:5]'
}
}
}
2
3
4
5
6
7
8
这会将 .title 编译成 .src-css-index--title--3cgV5
# 7、class 的组合
在 CSS Modules 中,一个选择器可以继承另一个选择器的规则
例如,在 index.css 中让 .title 继承 .red,使用 composes
.red{
color: red;
}
.title{
composes: red;
font-size: 20px;
font-weight: bold;
}
2
3
4
5
6
7
8
9
# 8、引入其他模块
选择器也可以继承其他 CSS 文件里的规则
color.css
.blue{
color: dodgerblue;
}
2
3
index.css
.red{
color: red;
}
.title{
composes: blue from './color.css';
font-size: 20px;
font-weight: bold;
}
2
3
4
5
6
7
8
9
css 文件自身继承和从其他文件引入的相同申明优先级:
.red{
color: red;
}
.title{
composes: red;
composes: blue from './color.css';
font-size: 20px;
font-weight: bold;
}
2
3
4
5
6
7
8
9
10
自身的优先级更高
# 9、使用变量
CSS Modules 支持使用变量,需要安装 PostCSS 和 postcss-modules-values
在 webpack.config.js 中引入 postcss-modules-values 并配置 postcss-loader
const values = require('postcss-modules-values');
...
{
loader: 'postcss-loader',
options: {
postcss: [
values
]
}
}
2
3
4
5
6
7
8
9
10
在 index.css 中定义变量并使用
@value dark-blue: #4e6ef2;
.title{
color: dark-blue;
}
2
3
4
5